#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>


#define NUM 1024
#define SIZE 32
#define SEP " "

char cmd_line[NUM];// 完整的命令行
char* g_argv[SIZE];// 打散后的命令行


// g_myval:环境变量用来测试
char g_myval[64];

#define INPUT_REDIR 1
#define OUTPUT_RESIR 2
#define APPEND_REDIR 3
#define NONE_REDIR 0
int redir_status = NONE_REDIR;

char *CheckReDir(char *str)
{
  assert(str);

  char *end = str + strlen(str) - 1;
  while(end >= str)
  {
    if(*end == '>')
    {
      // ls -a -l>myfile.txt
      // ls -a -l>>myfile.txt
      if(*(end - 1) == '>')
      {
        redir_status = APPEND_REDIR;
        *(end - 1) = '\0';
        end++;
        break;
      }
      // 只有一个 >
      redir_status = OUTPUT_RESIR;
      *end = '\0';
      end++;
      break;
    }
    else if(*end == '<')
    {
      redir_status = INPUT_REDIR;
      *end = '\0';
      end++;
      break;
    }
    else 
    {
      end--;
    }
    
  }
  if(end >= str)
  {
    // break 出来的
    return end;
  }
  else 
  {
    return NULL;
  }
}

// shell原理：子进程执行命令，父进程解析、等待 命令
int main()
{
  extern char **environ;
  // 0、命令行解释器是不退出的while(1)
  while(1)
  {
    // 1、打印提示信息
    printf("[zzy@local_make my_shell]-> ");
    fflush(stdout);
    memset(cmd_line, '\0', sizeof(cmd_line));

    // 2、获取用户的输入指令
    if(fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)
      continue;
    // ls -a -l\n\0  最后一个字符才是\n
    cmd_line[strlen(cmd_line) - 1] = '\0';
    // printf("echo: %s\n",cmd_line);
    // pid_t id = fork();
    //
    // 2.1 分析重定向
    char *sep = CheckReDir(cmd_line);
   //  if(sep != NULL)
   //  {
   //    // 重定向
   //      
   //  }
    
    // 3、命令行解析工作："ls -a -l" ---> "ls" "a" "-l"一个一个拆分使用
    g_argv[0] = strtok(cmd_line, SEP);// 第一次调用传原始字符串

    int index = 1;
    while(g_argv[index++] = strtok(NULL, SEP));// 第二次解析原来的字符串，第一个参数传NULL
    
    if(strcmp(g_argv[0], "export") == 0 && g_argv[1] != NULL)
    {
      int i = 0;

      strcpy(g_myval, g_argv[1]);
      int ret = putenv(g_myval);
      if(!ret)
        printf("%s export success\n", g_argv[1]);
      // for(i = 0; environ[i]; i++)
      //   printf("%d %s\n", i , environ[i]);
      continue;
    }
    

    // for(index = 0; g_argv[index]; index++)
    //   printf("g_arev[%d]: %s\n", index, g_argv[index]);

    //4、todo 内置命令：本质是shell内部的一个函数调用
    if(strcmp(g_argv[0], "cd") == 0)// 不想让子进程运行
    {
      if(g_argv[1] != NULL)
        chdir(g_argv[1]);// 更改路径

      continue;
    }
    //5、fork()
    pid_t id = fork();
    if(!id)
    {
      printf("子进程执行功能\n");
      if(sep != NULL)
      {
        // 曾经有重定向
        switch(redir_status)
        {
          case INPUT_REDIR:
            break;
          case OUTPUT_RESIR:
            int fd = open(sep, O_WRONLY | O_TRUNC | O_CREAT, 0666);
            break;
          case APPEND_REDIR:
            break;
          default:
            printf("bug\n");
            break;
        }
      }
      printf("child,MYVAL: %s\n",getenv("MYVAL"));
      printf("child,PATH: %s\n",getenv("PATH"));
      // execvpe(g_argv[0], g_argv, environ);
      execvp(g_argv[0], g_argv);
      exit(1);
    }

    // 父进程
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if(ret > 0)
      printf("exit code: %d\n",WEXITSTATUS(status));
  }
  return 0;
}
